Õppige, kuidas optimeerida Reacti kohandatud konkse, mõistes ja hallates sõltuvusi `useEffect` funktsioonis. Parandage jõudlust ja vältige levinud vigu.
Reacti kohandatud konksude sõltuvused: efekti optimeerimise valdamine jõudluse parandamiseks
Reacti kohandatud konksud on võimas vahend loogika abstraheerimiseks ja taaskasutamiseks teie komponentides. Kuid sõltuvuste ebaõige käsitsemine `useEffect` sees võib põhjustada jõudlusprobleeme, ebavajalikke uuesti renderdamisi ja isegi lõpmatuid tsükleid. See juhend pakub põhjaliku arusaama `useEffect` sõltuvustest ja parimatest tavadest kohandatud konksude optimeerimiseks.
Mõistmine useEffect ja sõltuvused
Reacti `useEffect` konks võimaldab teil komponentides teostada kõrvalmõjusid, nagu andmete hankimine, DOM-i manipuleerimine või tellimuste seadistamine. Teine argument `useEffect` funktsioonile on valikuline sõltuvuste massiiv. See massiiv ütleb Reactile, millal efekt peaks uuesti käivituma. Kui mõni sõltuvuste massiivi väärtustest muutub renderduste vahel, käivitatakse efekt uuesti. Kui sõltuvuste massiiv on tühi (`[]`), käivitub efekt ainult üks kord pärast esialgset renderdamist. Kui sõltuvuste massiiv jäetakse üldse ära, käivitub efekt pärast iga renderdamist.
Miks sõltuvused olulised on
Sõltuvused on üliolulised selle kontrollimiseks, millal teie efekt käivitub. Kui lisate sõltuvuse, mis tegelikult efekti käivitama ei pea, kaasneb sellega ebavajalik uuesti käivitamine, mis võib mõjutada jõudlust. Vastupidi, kui jätate välja sõltuvuse, mis *peab* efekti käivitama, ei pruugi teie komponent õigesti värskenduda, mis toob kaasa vigu ja ootamatu käitumise. Vaatame lihtsat näidet:
import React, { useState, useEffect } from 'react';
function ExampleComponent({ userId }) {
const [userData, setUserData] = useState(null);
useEffect(() => {
async function fetchData() {
const response = await fetch(`https://api.example.com/users/${userId}`);
const data = await response.json();
setUserData(data);
}
fetchData();
}, [userId]); // Dependency array: only re-run when userId changes
if (!userData) {
return <p>Loading...</p>;
}
return (
<div>
<h1>{userData.name}</h1>
<p>{userData.email}</p>
</div>
);
}
export default ExampleComponent;
Selles näites hangib efekt API-st kasutajaandmed. Sõltuvuste massiiv sisaldab `userId` elementi. See tagab, et efekt käivitub ainult siis, kui `userId` propp muutub. Kui `userId` jääb samaks, ei käivitu efekt uuesti, vältides ebavajalikke API-kõnesid.
Levinud vead ja kuidas neid vältida
Sõltuvustega `useEffect` töötamisel võib tekkida mitmeid levinud vigu. Nende vigade mõistmine ja vältimine on oluline tõhusa ja veavaba Reacti koodi kirjutamiseks.
1. Puuduvad sõltuvused
Kõige levinum viga on sõltuvuse väljajätmine, mis *peaks* olema sõltuvuste massiivis. See võib põhjustada aegunud sulguseid ja ootamatut käitumist. Näiteks:
import React, { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const intervalId = setInterval(() => {
setCount(count + 1); // Potential issue: `count` is not a dependency
}, 1000);
return () => clearInterval(intervalId);
}, []); // Empty dependency array: effect runs only once
return <p>Count: {count}</p>;
}
export default Counter;
Selles näites ei ole muutuja `count` sõltuvuste massiivis. Selle tulemusena kasutab `setInterval` tagasikutsumisfunktsioon alati `count` algväärtust (mis on 0). Loendur ei suurene õigesti. Õige versioon peaks sisaldama `count` sõltuvuste massiivis:
import React, { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const intervalId = setInterval(() => {
setCount(prevCount => prevCount + 1); // Correct: use functional update
}, 1000);
return () => clearInterval(intervalId);
}, []); // Now no dependency is needed since we use the functional update form.
return <p>Count: {count}</p>;
}
export default Counter;
Õppetund: Veenduge alati, et kõik efektis kasutatavad muutujad, mis on defineeritud väljaspool efekti skoopi, oleksid sõltuvuste massiivis. Võimaluse korral kasutage funktsionaalseid värskendusi (`setCount(prevCount => prevCount + 1)`) et vältida `count` sõltuvuse vajadust.
2. Ebavajalike sõltuvuste kaasamine
Ebavajalike sõltuvuste lisamine võib põhjustada ülemääraseid uuesti renderdamisi ja jõudluse halvenemist. Näiteks, kaaluge komponenti, mis võtab vastu proppina objekti:
import React, { useState, useEffect } from 'react';
function DisplayData({ data }) {
const [processedData, setProcessedData] = useState(null);
useEffect(() => {
// Perform some complex data processing
const result = processData(data);
setProcessedData(result);
}, [data]); // Problem: `data` is an object, so it changes on every render
function processData(data) {
// Complex data processing logic
return data;
}
if (!processedData) {
return <p>Loading...</p>;
}
return <p>{processedData.value}</p>;
}
export default DisplayData;
Sel juhul, isegi kui `data` objekti sisu jääb loogiliselt samaks, luuakse iga vanemkomponendi renderdamisel uus objekt. See tähendab, et `useEffect` käivitub uuesti iga renderdamise korral, isegi kui andmetöötlust tegelikult uuesti teha pole vaja. Siin on mõned strateegiad selle lahendamiseks:
Lahendus 1: Memoiseerimine `useMemo` abil
Kasutage `useMemo` funktsiooni `data` proppi memoiseerimiseks. See loob `data` objekti uuesti ainult siis, kui selle olulised omadused muutuvad.
import React, { useState, useEffect, useMemo } from 'react';
function ParentComponent() {
const [value, setValue] = useState(0);
// Memoize the `data` object
const data = useMemo(() => ({ value }), [value]);
return <DisplayData data={data} />;
}
function DisplayData({ data }) {
const [processedData, setProcessedData] = useState(null);
useEffect(() => {
// Perform some complex data processing
const result = processData(data);
setProcessedData(result);
}, [data]); // Now `data` only changes when `value` changes
function processData(data) {
// Complex data processing logic
return data;
}
if (!processedData) {
return <p>Loading...</p>;
}
return <p>{processedData.value}</p>;
}
export default ParentComponent;
Lahendus 2: Propi dekonstrueerimine
Andke `data` objekti üksikud omadused proppidena edasi terve objekti asemel. See võimaldab `useEffect` funktsioonil uuesti käivituda ainult siis, kui konkreetsed omadused, millest see sõltub, muutuvad.
import React, { useState, useEffect } from 'react';
function ParentComponent() {
const [value, setValue] = useState(0);
return <DisplayData value={value} />; // Pass `value` directly
}
function DisplayData({ value }) {
const [processedData, setProcessedData] = useState(null);
useEffect(() => {
// Perform some complex data processing
const result = processData(value);
setProcessedData(result);
}, [value]); // Only re-run when `value` changes
function processData(value) {
// Complex data processing logic
return { value }; // Wrap in object if needed inside DisplayData
}
if (!processedData) {
return <p>Loading...</p>;
}
return <p>{processedData.value}</p>;
}
export default ParentComponent;
Lahendus 3: Väärtuste võrdlemine `useRef` abil
Kui teil on vaja võrrelda `data` objekti *sisu* ja käivitada efekt uuesti ainult siis, kui sisu muutub, saate kasutada `useRef` funktsiooni `data` eelmise väärtuse salvestamiseks ja teostada sügavvõrdluse.
import React, { useState, useEffect, useRef } from 'react';
import { isEqual } from 'lodash'; // Requires lodash library (npm install lodash)
function DisplayData({ data }) {
const [processedData, setProcessedData] = useState(null);
const previousData = useRef(data);
useEffect(() => {
if (!isEqual(data, previousData.current)) {
// Perform some complex data processing
const result = processData(data);
setProcessedData(result);
previousData.current = data;
}
}, [data]); // `data` is still in the dependency array, but we check for deep equality
function processData(data) {
// Complex data processing logic
return data;
}
if (!processedData) {
return <p>Loading...</p>;
}
return <p>{processedData.value}</p>;
}
export default DisplayData;
Märkus: Sügavvõrdlused võivad olla kulukad, seega kasutage seda lähenemist mõistlikult. Samuti tugineb see näide `lodash` teegile. Saate selle installida, kasutades `npm install lodash` või `yarn add lodash`.
Õppetund: Kaaluge hoolikalt, millised sõltuvused on tegelikult vajalikud. Vältige objektide või massiivide kaasamiust, mis luuakse uuesti iga renderdamise korral, kui nende sisu jääb loogiliselt samaks. Kasutage jõudluse optimeerimiseks memoiseerimist, dekonstrueerimist või sügavvõrdluse tehnikaid.
3. Lõpmatud tsüklid
Sõltuvuste vale haldamine võib viia lõpmatute tsükliteni, kus `useEffect` konks käivitub pidevalt uuesti, põhjustades teie komponendi külmumise või kokkujooksmise. See juhtub sageli siis, kui efekt värskendab olekumuutujat, mis on ühtlasi efekti sõltuvus. Näiteks:
import React, { useState, useEffect } from 'react';
function InfiniteLoop() {
const [data, setData] = useState(null);
useEffect(() => {
// Fetch data from an API
fetch('https://api.example.com/data')
.then(response => response.json())
.then(result => {
setData(result); // Updates `data` state
});
}, [data]); // Problem: `data` is a dependency, so the effect re-runs when `data` changes
if (!data) {
return <p>Loading...</p>;
}
return <p>{data.value}</p>;
}
export default InfiniteLoop;
Selles näites hangib efekt andmed ja määrab need `data` olekumuutujale. Kuid `data` on ka efekti sõltuvus. See tähendab, et iga kord, kui `data` värskendatakse, käivitub efekt uuesti, hangib andmed uuesti ja määrab `data` uuesti, mis viib lõpmatu tsüklini. Selle lahendamiseks on mitu võimalust:
Lahendus 1: Tühi sõltuvuste massiiv (ainult esialgne laadimine)
Kui soovite andmeid ainult üks kord komponendi paigaldamisel hankida, saate kasutada tühja sõltuvuste massiivi:
import React, { useState, useEffect } from 'react';
function InfiniteLoop() {
const [data, setData] = useState(null);
useEffect(() => {
// Fetch data from an API
fetch('https://api.example.com/data')
.then(response => response.json())
.then(result => {
setData(result);
});
}, []); // Empty dependency array: effect runs only once
if (!data) {
return <p>Loading...</p>;
}
return <p>{data.value}</p>;
}
export default InfiniteLoop;
Lahendus 2: Kasuta laadimiseks eraldi olekut
Kasutage eraldi olekumuutujat, et jälgida, kas andmed on laaditud. See takistab efekti uuesti käivitumist, kui `data` olek muutub.
import React, { useState, useEffect } from 'react';
function InfiniteLoop() {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
if (isLoading) {
// Fetch data from an API
fetch('https://api.example.com/data')
.then(response => response.json())
.then(result => {
setData(result);
setIsLoading(false);
});
}
}, [isLoading]); // Only re-run when `isLoading` changes
if (!data) {
return <p>Loading...</p>;
}
return <p>{data.value}</p>;
}
export default InfiniteLoop;
Lahendus 3: Tingimuslik andmete hankimine
Hankige andmed ainult siis, kui need on praegu null. See takistab järgmisi hankimisi pärast esialgsete andmete laadimist.
import React, { useState, useEffect } from 'react';
function InfiniteLoop() {
const [data, setData] = useState(null);
useEffect(() => {
if (!data) {
// Fetch data from an API
fetch('https://api.example.com/data')
.then(response => response.json())
.then(result => {
setData(result);
});
}
}, [data]); // `data` is still a dependency but the effect is conditional
if (!data) {
return <p>Loading...</p>;
}
return <p>{data.value}</p>;
}
export default InfiniteLoop;
Õppetund: Olge äärmiselt ettevaatlik olekumuutuja värskendamisel, mis on ühtlasi efekti sõltuvus. Lõpmatute tsüklite vältimiseks kasutage tühje sõltuvuste massiive, eraldi laadimisolekuid või tingimuslikku loogikat.
4. Muudetavad objektid ja massiivid
Muudetavate objektide või massiividega sõltuvustena töötades ei käivita objekti omaduste või massiivi elementide muutused efekti automaatselt. Seda seetõttu, et React teostab sõltuvuste pinnavõrdluse.
import React, { useState, useEffect } from 'react';
function MutableObject() {
const [config, setConfig] = useState({ theme: 'light', language: 'en' });
useEffect(() => {
console.log('Config changed:', config);
}, [config]); // Problem: Changes to `config.theme` or `config.language` won't trigger the effect
const toggleTheme = () => {
// Mutating the object
config.theme = config.theme === 'light' ? 'dark' : 'light';
setConfig(config); // This won't trigger a re-render or the effect
};
return (
<div>
<p>Theme: {config.theme}, Language: {config.language}</p>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}
export default MutableObject;
Selles näites muudab funktsioon `toggleTheme` otse `config` objekti, mis on halb tava. Reacti pinnavõrdlus näeb, et `config` on mälu järgi endiselt *sama* objekt, kuigi selle omadused on muutunud. Selle parandamiseks peate oleku värskendamisel looma *uue* objekti:
import React, { useState, useEffect } from 'react';
function MutableObject() {
const [config, setConfig] = useState({ theme: 'light', language: 'en' });
useEffect(() => {
console.log('Config changed:', config);
}, [config]); // Now the effect will trigger when `config` changes
const toggleTheme = () => {
setConfig({ ...config, theme: config.theme === 'light' ? 'dark' : 'light' }); // Create a new object
};
return (
<div>
<p>Theme: {config.theme}, Language: {config.language}</p>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}
export default MutableObject;
Kasutades spread operaatorit (`...config`), loome uue objekti värskendatud `theme` omadusega. See käivitab uuesti renderdamise ja efekt käivitatakse uuesti.
Õppetund: Kohelge olekumuutujaid alati muutumatutena. Objektide või massiivide värskendamisel looge uued eksemplarid olemasolevate muutmine asemel. Kasutage spread operaatorit (`...`), `Array.map()`, `Array.filter()` või sarnaseid tehnikaid uute koopiate loomiseks.
Kohandatud konksude optimeerimine sõltuvustega
Nüüd, kui oleme mõistnud levinud vigu, vaatame, kuidas optimeerida kohandatud konkse sõltuvuste hoolika haldamise abil.
1. Funktsioonide memoiseerimine `useCallback` abil
Kui teie kohandatud konks tagastab funktsiooni, mida kasutatakse sõltuvusena teises `useEffect` funktsioonis, peaksite funktsiooni memoiseerima `useCallback` abil. See takistab funktsiooni uuesti loomist iga renderdamise korral, mis käivitaks efekti ebavajalikult.
import React, { useState, useEffect, useCallback } from 'react';
function useFetchData(url) {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const fetchData = useCallback(async () => {
setIsLoading(true);
try {
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setIsLoading(false);
}
}, [url]); // Memoize `fetchData` based on `url`
useEffect(() => {
fetchData();
}, [fetchData]); // Now `fetchData` only changes when `url` changes
return { data, isLoading, error };
}
function MyComponent() {
const [userId, setUserId] = useState(1);
const { data, isLoading, error } = useFetchData(`https://api.example.com/users/${userId}`);
return (
<div>
{/* ... */}
</div>
);
}
export default MyComponent;
Selles näites on `fetchData` funktsioon memoiseeritud `useCallback` abil. Sõltuvuste massiiv sisaldab `url` elementi, mis on ainus muutuja, mis mõjutab funktsiooni käitumist. See tagab, et `fetchData` muutub ainult siis, kui `url` muutub. Seega käivitub `useEffect` konks `useFetchData` sees uuesti ainult siis, kui `url` muutub.
2. `useRef` kasutamine stabiilsete viidete jaoks
Mõnikord on vaja efektis juurde pääseda proppi või olekumuutuja viimasele väärtusele, kuid te ei soovi, et efekt selle väärtuse muutumisel uuesti käivituks. Sel juhul saate kasutada `useRef` funktsiooni väärtusele stabiilse viite loomiseks.
import React, { useState, useEffect, useRef } from 'react';
function LogLatestValue({ value }) {
const latestValue = useRef(value);
useEffect(() => {
latestValue.current = value; // Update the ref on every render
}, [value]); // Update the ref when `value` changes
useEffect(() => {
// Log the latest value after 5 seconds
const timerId = setTimeout(() => {
console.log('Latest value:', latestValue.current); // Access the latest value from the ref
}, 5000);
return () => clearTimeout(timerId);
}, []); // Effect runs only once on mount
return <p>Value: {value}</p>;
}
export default LogLatestValue;
Selles näites värskendatakse `latestValue` referentsi iga renderdamise korral `value` proppi praeguse väärtusega. Kuid efekti, mis väärtust logib, käivitatakse tänu tühjale sõltuvuste massiivile ainult üks kord paigaldamisel. Efekti sees saame juurde pääseda viimasele väärtusele `latestValue.current` abil. See võimaldab meil juurde pääseda `value` kõige ajakohasematele väärtusele, ilma et efekt peaks iga kord `value` muutumisel uuesti käivituma.
3. Kohandatud abstraktsioonide loomine
Looge kohandatud võrdlusfunktsioon või abstraktsioon, kui töötate objektiga ja ainult väike osa selle omadustest on `useEffect` väljakutsete jaoks oluline.
import React, { useState, useEffect } from 'react';
// Custom comparator to only track theme changes.
function useTheme(config) {
const [theme, setTheme] = useState(config.theme);
useEffect(() => {
setTheme(config.theme);
}, [config.theme]);
return theme;
}
function ConfigComponent({ config }) {
const theme = useTheme(config);
return (
<p>The current theme is {theme}</p>
)
}
export default ConfigComponent;
Õppetund: Kasutage `useCallback` funktsiooni sõltuvustena kasutatavate funktsioonide memoiseerimiseks. Kasutage `useRef` funktsiooni stabiilsete viidete loomiseks väärtustele, millele peate efektides juurde pääsema, ilma et efektid uuesti käivituksid. Keeruliste objektide või massiividega tegeledes kaaluge kohandatud võrdlusfunktsioonide või abstraktsioonikihtide loomist, et efekte käivitada ainult siis, kui asjakohased omadused muutuvad.
Ăślemaailmsed kaalutlused
Reacti rakenduste arendamisel ülemaailmsele publikule on oluline arvestada, kuidas sõltuvused võivad mõjutada lokaliseerimist ja rahvusvahelistumist. Siin on mõned olulised kaalutlused:
1. Lokaadi muutused
Kui teie komponent sõltub kasutaja lokaadist (nt kuupäevade, numbrite või valuutade vormindamiseks), peaksite lokaadi lisama sõltuvuste massiivi. See tagab, et efekt käivitub uuesti lokaadi muutumisel, värskendades komponenti õige vorminguga.
import React, { useState, useEffect } from 'react';
import { format } from 'date-fns'; // Requires date-fns library (npm install date-fns)
function LocalizedDate({ date, locale }) {
const [formattedDate, setFormattedDate] = useState('');
useEffect(() => {
setFormattedDate(format(date, 'PPPP', { locale }));
}, [date, locale]); // Re-run when `date` or `locale` changes
return <p>{formattedDate}</p>;
}
export default LocalizedDate;
Selles näites kasutatakse `date-fns` teegi funktsiooni `format` kuupäeva vormindamiseks vastavalt määratud lokaadile. `locale` on kaasatud sõltuvuste massiivi, nii et efekt käivitub uuesti lokaadi muutumisel, värskendades vormindatud kuupäeva.
2. Ajavööndi kaalutlused
Kuupäevade ja kellaaegadega töötades olge ajavöönditega ettevaatlik. Kui teie komponent kuvab kuupäevi või kellaaegu kasutaja kohalikus ajavööndis, peate võib-olla ajavööndi lisama sõltuvuste massiivi. Kuid ajavööndi muutused on harvemad kui lokaadi muutused, seega võiksite ajavööndi värskendamiseks kaaluda eraldi mehhanismi, näiteks globaalset konteksti.
3. Valuuta vormindamine
Valuutade vormindamisel kasutage õiget valuutakoodi ja lokaati. Lisage mõlemad sõltuvuste massiivi, et tagada valuuta õige vormindamine kasutaja piirkonna jaoks.
import React, { useState, useEffect } from 'react';
function LocalizedCurrency({ amount, currency, locale }) {
const [formattedCurrency, setFormattedCurrency] = useState('');
useEffect(() => {
setFormattedCurrency(new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount));
}, [amount, currency, locale]); // Re-run when `amount`, `currency`, or `locale` changes
return <p>{formattedCurrency}</p>;
}
export default LocalizedCurrency;
Õppetund: Globaalsele publikule arendades kaaluge alati, kuidas sõltuvused võivad mõjutada lokaliseerimist ja rahvusvahelistumist. Kaasake lokaat, ajavöönd ja valuutakood sõltuvuste massiivi, kui see on vajalik, et tagada komponentide õige andmekuvamine kasutajatele erinevates piirkondades.
Järeldus
Sõltuvuste haldamise valdamine `useEffect` konksus on ülioluline tõhusate, veavabade ja suure jõudlusega Reacti kohandatud konksude kirjutamiseks. Mõistes levinud vigu ja rakendades käesolevas juhendis käsitletud optimeerimistehnikaid, saate luua kohandatud konkse, mis on nii taaskasutatavad kui ka hooldatavad. Pidage meeles hoolikalt kaaluda, millised sõltuvused on tegelikult vajalikud, kasutage vajaduse korral memoiseerimist ja stabiilseid viiteid ning olge teadlik globaalsetest kaalutlustest, nagu lokaliseerimine ja rahvusvahelistumine. Järgides neid parimaid tavasid, saate avada Reacti kohandatud konksude täieliku potentsiaali ja luua kvaliteetseid rakendusi ülemaailmsele publikule.
See põhjalik juhend on käsitlenud palju teemasid. Kokkuvõtteks on siin peamised õppetunnid:
- Mõistke sõltuvuste eesmärki: Need kontrollivad, millal teie efekt käivitub.
- Vältige puuduvaid sõltuvusi: Veenduge, et kõik efekti sees kasutatavad muutujad on kaasatud.
- Välistage ebavajalikud sõltuvused: Kasutage memoiseerimist, dekonstrueerimist või sügavvõrdlust.
- Vältige lõpmatuid tsükleid: Olge ettevaatlik olekumuutujate värskendamisel, mis on ühtlasi sõltuvused.
- Kohelge olekut muutumatuna: Looge värskendamisel uued objektid või massiivid.
- Memoiseerige funktsioonid `useCallback` abil: Vältige ebavajalikke uuesti renderdamisi.
- Kasutage `useRef` stabiilsete viidete jaoks: Juurdepääs viimasele väärtusele ilma uuesti renderdamisi käivitamata.
- Kaaluge globaalseid tagajärgi: Arvestage lokaadi, ajavööndi ja valuutamuutustega.
Nende põhimõtete rakendamisega saate kirjutada tugevamaid ja tõhusamaid Reacti kohandatud konkse, mis parandavad teie rakenduste jõudlust ja hooldatavust.